/**
 * websocket网络层
 */
var NetWork = cc.Class.extend({
    /**
     * socket client
     * @type {Paho.MQTT.Client}
     * @private
     */
    _client: null,
    /**
     * 是否是联接状态
     * @type {Boolean}
     * @private
     */
    _isConnected: false,
    /**
     * 回调集合
     * @type {Array}
     * @private
     */
    _listenerMap: [],
    /**
     * 消息队列
     * @type {Array}
     * @private
     */
    _msgQueue: [],
    /**
     * 轮询
     * @type {Number}
     * @private
     */
    _timer: null,
    /**
     * 断线回调
     * @type {Function}
     * @private
     */
    _disconnectCb: null,
    /**
     * 联接成功回调
     */
    _successCb: null,
    /**
     * 联接失败回调
     */
    _failureCb: null,
    /**
     * 是否是停止状态
     * @type {Boolean}
     * @private
     */
    _isStop: false,
    /**
     * 排除列表
     * @type {Array}
     * @private
     */
    _excepts: [],
    /**
     * 是否是大厅
     * @private
     * */
    _is_hall: false,
    /**
     * 默认的排除列表
     * @type {Array}
     * @const
     * @private
     */
    DEFAULT_EXCEPTS: [3007, 3008, 4002, 4020, 4990],
    /**
     * 轮询频率
     * @type {Number}
     * @const
     * @private
     */
    FREQUENCY: 50,
    /**
     * 发送前置消息列表
     */
    _beforeSendCallbackMap: {},
    /**
     * 初始化
     */
    ctor: function () {
        this.clearMsgs();
    },
    /**
     * 清理参数
     * @public
     */
    clearMsgs: function () {
        this._listenerMap = [];
        this._excepts = [];
        this._msgQueue = [];
        this._isStop = false;
        if (!this._timer) {
            clearInterval(this._timer);
        }
    },
    /**
     * 添加前置消息协议
     * @param code
     * @param callback
     */
    regBeforeSendCallback: function (code, callback) {
        this._beforeSendCallbackMap[code] = callback;
    },
    /**
     * 取得消息队列长度
     * @return {Number}
     * @public
     */
    getMsgQueueNum: function () {
        return this._msgQueue.length;
    },
    /**
     * 停止监听消息
     * @param {Array} [e] 排除消息
     * @public
     */
    stop: function (e) {
        this._excepts = (e || []).concat(this.DEFAULT_EXCEPTS);
        this._isStop = true;
        console.log('network stop()');
    },
    /**
     * 开始监听消息
     * @public
     */
    start: function () {
        this._excepts = [];
        this._isStop = false;
        console.log('network start()');
    },
    /**
     * 轮询函数
     * @private
     */
    _update: function () {
        while (this._msgQueue.length > 0) {
            var obj = this._msgQueue[0];
            if (this._isStop && this._excepts.indexOf(obj.code) < 0) {
                break;
            }
            try {
                this._msgQueue.shift();
                if (obj['base64data']) { // 处理go服务器消息
                    console.log('base64data:' + obj['base64data']);
                    if (obj['protoc'] == 'cp' || obj['protoc'] == 'phz') {
                        var messageType = ProtoTypes['paohuzi'][obj.code.toString()];
                        if (!messageType) {
                            continue;
                        }
                        var builder = mProto.builder['paohuzi'].build('paohuzi.' + messageType);
                        if (!builder) {
                            cc.error('未找到正确解析：' + 'paohuzi.' + messageType);
                            continue;
                        }
                        var msgArr = base64ToArrayBuffer(obj['base64data']);
                        var msgObj = builder.decode(msgArr);
                        // cc.log(timestamp2time(getCurTimestamp()) + '=code=' + obj.code + ' recv: ' + JSON.stringify(msgObj));
                        if (cc.sys.isNative) {
                            console.log(timestamp2time(getCurTimestamp()) + ' recv: ' + JSON.stringify(obj));
                        } else {
                            console.log(
                                '%c[' + timestamp2time(getCurTimestamp()) + '] %crecv%c ' + JSON.stringify(obj),
                                'background-color:#dfdfdf; color:blue',
                                'background-color:grey; color:blue;',
                                'color:black'
                            );
                        }

                        if (obj.code == 101 && ReporterType && ReporterType[3001] && window[ReporterType[3001]]) {
                            if (window.touch_createRoom && !window.time_createRoom) {
                                window.time_createRoom = new Date().valueOf() - window.touch_createRoom;
                                window.networkEnd_createRoom = new Date().valueOf();
                            }
                            YYReporterUtil.add(ReporterType[3001], (getCurrentTimeMills() - window[ReporterType[3001]]));
                            YYReporterUtil.add(3001, 1);
                            window[ReporterType[3001]] = null;
                        }

                        eventManager.dispatchEvent('GS_Net_' + obj.code, msgObj);
                    } else if (obj['protoc'] && mProto.builder[obj['protoc']]) {
                        var messageType = ProtoTypes[obj['protoc']][obj.code.toString()];
                        if (!messageType) {
                            continue;
                        }
                        var builder = mProto.builder[obj['protoc']].build(obj['protoc'] + '.' + messageType);
                        if (!builder) {
                            cc.error('未找到正确解析：' + obj['protoc'] + '.' + messageType);
                            continue;
                        }
                        var msgArr = base64ToArrayBuffer(obj['base64data']);
                        var msgObj = builder.decode(msgArr);

                        cc.log(timestamp2time(getCurTimestamp()) + '=code=' + obj.code + ' recv: ' + JSON.stringify(msgObj));
                        eventManager.dispatchEvent('GS_Net_' + obj.code, msgObj);
                    } else {
                        try {
                            if (!ProtobufferTables[obj.code]) {
                                cc.error('未找到正确解析：' + JSON.stringify(ProtobufferTables) + '.' + obj.code);
                                continue;
                            }
                            var d_code = ProtobufferTables[obj.code].split('.')[0];
                            var builder = mProto.builder[d_code].build(ProtobufferTables[obj.code]);
                            if (!builder) {
                                cc.error('未找到正确解析：' + obj['protoc'] + '.' + messageType);
                                continue;
                            }
                            var msgArr = base64ToArrayBuffer(obj['base64data']);
                            var msgObj = builder.decode(msgArr);
                            console.log(timestamp2time(getCurTimestamp()) + '==' + (new Date().getTime()) + '=code=' + obj.code + ' recv: ' + JSON.stringify(msgObj));
                            if (msgObj && msgObj.errorCode > 0 && msgObj.errorMsg) {
                                showAlert(msgObj.errorMsg);
                                break;
                            }
                            eventManager.dispatchEvent('Msg_Net_' + obj.code, msgObj);
                        } catch (e) {
                            alertError(obj.code, e, obj);
                        }
                    }

                } else { // 处理java服务器消息
                    obj.errorCode = obj.errorCode || 0;
                    // cc.log(timestamp2time(getCurTimestamp()) + '=code=' + obj.code + ' recv: ' + JSON.stringify(obj));
                    if (cc.sys.isNative) {
                        console.log(timestamp2time(getCurTimestamp()) + ' recv: ' + JSON.stringify(obj));
                    } else {
                        console.log(
                            '%c[' + timestamp2time(getCurTimestamp()) + '] %crecv%c ' + JSON.stringify(obj),
                            'background-color:#dfdfdf; color:blue',
                            'background-color:grey; color:blue;',
                            'color:black'
                        );
                    }
                    if (obj.code == 3002 && ReporterType && ReporterType[3001] && window[ReporterType[3001]]) {
                        window.time_createRoom = (getCurrentTimeMills() - window[ReporterType[3001]]);
                        window.networkEnd_createRoom = new Date().valueOf();
                        YYReporterUtil.add(ReporterType[3001], (getCurrentTimeMills() - window[ReporterType[3001]]));
                        YYReporterUtil.add(3001, 1);
                        window[ReporterType[3001]] = null;
                    }
                    if (ReporterType && ReporterType[obj.code] && window[ReporterType[obj.code]]) {
                        if (window.touch_createRoom && !window.time_createRoom && obj.code == 3002) {
                            window.time_createRoom = new Date().valueOf() - window.touch_createRoom;
                            window.networkEnd_createRoom = new Date().valueOf();
                        }
                        YYReporterUtil.add(ReporterType[obj.code], (getCurrentTimeMills() - window[ReporterType[obj.code]]));
                        YYReporterUtil.add(obj.code, 1);
                        window[ReporterType[obj.code]] = null;
                    }
                    if (obj.errorCode && obj.errorMsg) {
                        showAlert(obj.errorMsg);
                        break;
                    }
                    eventManager.dispatchEvent('Msg_Net_' + obj.code, obj.data, obj.errorCode);
                }
            } catch (e) {
                alertError(obj.code, e, obj);
            }
            break;
        }
    },
    /**
     * 联接成功
     * @private
     */
    _onConnectSuccess: function () {
        this._client.subscribe('n');
        // cc.log('onConnect');
        this._isConnected = true;
        this._timer = setInterval(this._update.bind(this), this.FREQUENCY);
    },
    /**
     * 联接失败
     * @private
     */
    _onConnectFailure: function () {
        this._isConnected = false;
    },
    /**
     * 联接失效
     * @param responseObject
     * @private
     */
    _onConnectionLost: function (responseObject) {
        YYReporterUtil.add(ReporterType.Disconnect_ConnectionLost, 1);
        clearInterval(this._timer);
        this.clearMsgs();
        if (responseObject.errorCode !== 0) {
            cc.error('onConnectionLost:' + responseObject.errorMessage);
        }
        this._isConnected = false;
        if (this._disconnectCb) {
            this._disconnectCb();
        } else {
            showBoard('LoginBoard');
        }
    },
    /**
     * 收到消息
     * @param message
     * @private
     */
    _onMessageArrived: function (message) {
        var bytes = message.payloadBytes;
        var arr = [];
        try {
            for (var j = 2; j < bytes.length; j++)
                arr.push(bytes[j]);
            arr = bzip2.simple(bzip2.array(arr));
            arr = UTF8ArrayToUTF16Array(arr, 0);
        } catch (e) {
            arr = UTF8ArrayToUTF16Array(bytes, 2);
        }
        var str = decodeURIComponent(arr);

        try {
            var obj = JSON.parse(str);
            this._msgQueue.push(obj);
        } catch (e) {
            cc.log(str);
            cc.log(e);
        }
    },
    /**
     * 接收事件
     * @param obj
     * @public
     */
    recv: function (obj) {
        this._msgQueue.push(obj);
        this._update();
    },
    /**
     * 接收事件
     * @param obj
     * @public
     */
    selfRecv: function (obj) {
        this._msgQueue.push(obj);
    },
    /**
     * 添加监听
     * @param {Number} code
     * @param {Function} cb
     * @param {cc.Node} [target]
     * @public
     */
    addListener: function (code, cb, target) {
        var key = 'Net_';
        if (code > 1000) {
            key = 'Msg_' + key + code;
        } else {
            key = 'GS_' + key + code;
        }
        eventManager.addListener(key, cb, target);
    },
    /**
     * 删除监听
     * @param {Number} code
     * @param {cc.Node} [target]
     * @public
     */
    removeListener: function (code, target) {
        var key = 'Net_';
        if (code > 1000) {
            key = 'Msg_' + key + code;
        } else {
            key = 'GS_' + key + code;
        }
        eventManager.removeListener(key, target);
    },
    /**
     * 删除监听数组
     * @param {Array} arr
     * @param {cc.Node} [target]
     * @public
     */
    removeListeners: function (arr, target) {
        for (var i = 0; i < arr.length; i++) {
            var code = arr[i];
            this.removeListener(code, target);
        }
    },
    /**
     * 删除监听数组
     * @param {cc.Node} target
     * @public
     */
    removeListenersForTarget: function (target) {
        eventManager.removeListeners(target);
    },
    /**
     * 发送java服务器数据
     * @param {Number} code
     * @param {Object} data
     * @public
     */
    send: function (code, data) {
        if (!this._isConnected) {
            return;
        }
        if (this._beforeSendCallbackMap[code]) {
            this._beforeSendCallbackMap[code](data);
        }
        var obj = {};
        obj.code = code;
        obj.data = data || {};
        if (gameData.uid) {
            obj.uid = gameData.uid;
        }
        if (gameData.gid) {
            obj.gid = gameData.gid;
        }
        if (obj.code == 2103) {
            obj.data.cv = ClubDataManager.getClubVersion();
        }
        if (ReporterType && ReporterType[code]) {
            if (code == 3001) {
                window.time_createRoom = null;
                window.touch_createRoom = new Date().valueOf();
            }
            window[ReporterType[code]] = getCurrentTimeMills();
        }
        SubUpdateUtil.filterMessage(obj);
        var msgStr = JSON.stringify(obj);
        var sum = 0;
        for (var i = 0; i < msgStr.length; i++) {
            if (msgStr.charCodeAt(i) < 256)
                sum += msgStr.charCodeAt(i);
        }
        var sign = (sum + 245619426) % 15619472;
        // sign = '';
        var message = new Paho.MQTT.Message(sign + msgStr);
        message.destinationName = 'n';
        this._client.send(message);
        // cc.log('send: ' + JSON.stringify(obj));
        if (cc.sys.isNative) {
            console.log(timestamp2time(getCurTimestamp()) + ' send: ' + JSON.stringify(obj));
        } else {
            console.log('%c[' + timestamp2time(getCurTimestamp()) + '] %csend%c ' + JSON.stringify(obj),
                'background-color:#dfdfdf; color:red',
                'background-color:#dfdfdf; color:red;',
                'color:black'
            );
        }
    },
    /**
     * 针对大厅通用功能的send
     * @param {Number | String} code
     * @param {Object} [data]
     * @public
     */
    sendProto: function (code, data) {
        if (!this._isConnected) {
            return;
        }
        var obj = {};
        if (cc.isUndefined(data)) {
            obj.code = 5000;
            obj.dt = code;
            obj.data = {room_id: gameData.roomId};
        } else {
            obj.code = code;
            obj.data = data || {};
        }
        if (gameData.uid) {
            obj.uid = gameData.uid;
        }
        if (gameData.gid) {
            obj.gid = gameData.gid;
        }

        var msgStr = JSON.stringify(obj);
        var sum = 0;
        for (var i = 0; i < msgStr.length; i++) {
            if (msgStr.charCodeAt(i) < 256)
                sum += msgStr.charCodeAt(i);
        }
        var sign = (sum + 245619426) % 15619472;
        // sign = '';
        var message = new Paho.MQTT.Message(sign + msgStr);
        message.destinationName = 'n';
        this._client.send(message);
        cc.log('send: ' + JSON.stringify(obj));
    },
    /**
     * 针对道具表情使用的send
     * 这里需要在3018发送的data的同级 包个dt给服务器
     * @param {Number} code
     * @param {Object} data
     * @public
     */
    sendEmoji: function (code, data, dt) {
        if (!this._isConnected) {
            return;
        }
        var obj = {};
        obj.code = code;
        obj.dt = dt;
        obj.data = data || {};
        if (gameData.uid) {
            obj.uid = gameData.uid;
        }
        if (gameData.gid) {
            obj.gid = gameData.gid;
        }
        if (cc.sys.isNative && !DeviceUtil.getDebug()) {
            obj.data.map_ver = getLocalVersion();
        }
        var msgStr = JSON.stringify(obj);
        var sum = 0;
        for (var i = 0; i < msgStr.length; i++) {
            if (msgStr.charCodeAt(i) < 256)
                sum += msgStr.charCodeAt(i);
        }
        var sign = (sum + 245619426) % 15619472;
        // sign = '';
        var message = new Paho.MQTT.Message(sign + msgStr);
        message.destinationName = 'n';
        this._client.send(message);
        cc.log('send: ' + JSON.stringify(obj));
    },

    /**
     * 发送给go服务器数据
     * @param {Number} code
     * @param {String} dt
     * @private
     */
    _sendPhz: function (code, dt) {
        if (!this._isConnected) {
            return;
        }
        var obj = {};
        obj.code = code;
        obj.dt = dt;
        obj.data = {room_id: gameData.roomId};
        if (gameData.uid) {
            obj.uid = gameData.uid;
        }
        if (gameData.gid) {
            obj.gid = gameData.gid;
        }
        if (cc.sys.isNative && !DeviceUtil.getDebug()) {
            // if (app.appId == APP_ID.PENGHUZI) {
            //     obj.data.map_ver2 = getLocalVersion();
            // } else {
            //     obj.data.map_ver = getLocalVersion();
            // }
            obj.data.map_ver = getLocalVersion();
        }
        var msgStr = JSON.stringify(obj);
        var sum = 0;
        for (var i = 0; i < msgStr.length; i++) {
            if (msgStr.charCodeAt(i) < 256)
                sum += msgStr.charCodeAt(i);
        }
        var sign = (sum + 245619426) % 15619472;
        // sign = '';
        var message = new Paho.MQTT.Message(sign + msgStr);
        message.destinationName = 'n';
        this._client.send(message);
        cc.log('send: ' + msgStr);
    },
    sendPhz: function (code, dt) {
        network._sendPhz(code, dt);
    },
    /**
     * 联接服务器
     * @param {String} clientId
     * @param {Function} successCB
     * @param {Function} failureCB
     * @param {String} ip
     * @public
     */
    connect: function (clientId, successCB, failureCB, ip) {
        if (this._isConnected) {
            return;
        }


        this._successCb = successCB;
        this._failureCb = failureCB;
        var url = gameData.url;
        var port = app.port;
        if(!cc.sys.isNative){
            ip = app.gameUrl ;
        }
        if (window.inReview && app.reviewUrl) {
            ip = app.reviewUrl;
        }
        if (app.ceshiUrl) {
            ip = app.ceshiUrl;
        }
        this._ip = ip;
        this.currentIp = ip.replace(/\./g, "_");
        //report
        this.conenctStartTime = (new Date().valueOf());
        YYReporterUtil.add('cnct_cnt', 1);
        YYReporterUtil.add('cnct_cnt_' + this.currentIp, 1);

        url = url || 'ws://' + ip + ':' + port + '/mqtt';
        cc.log(url);
        this._client = new Paho.MQTT.Client(url, clientId);
        this._client.onConnectionLost = this._onConnectionLost.bind(this);
        this._client.onMessageArrived = this._onMessageArrived.bind(this);
        this._client.connect({
            timeout: 4,
            mqttVersion: 3,
            keepAliveInterval: 8,
            onSuccess: this._connectSuccess.bind(this),
            onFailure: this._connectFailure.bind(this)
        });
    },
    /**
     * 联接成功
     * @private
     */
    _connectSuccess: function () {
        saveSuccesIpLists(this._ip, ((new Date().valueOf()) - this.conenctStartTime));
        //report
        YYReporterUtil.addStep('cnct_succ_time', (new Date().valueOf()) - this.conenctStartTime);
        YYReporterUtil.add('cnct_succ', 1);

        YYReporterUtil.addStep('cnct_succ_time_' + this.currentIp, (new Date().valueOf()) - this.conenctStartTime);
        YYReporterUtil.add('cnct_succ_' + this.currentIp, 1);

        this._onConnectSuccess();
        if (this._successCb) {
            this._successCb();
        }
    },
    /**
     * 联接失败
     * @private
     */
    _connectFailure: function () {
        clearSuccesIpLists(this._ip);
        //report
        YYReporterUtil.addStep('cnct_fail_time', (new Date().valueOf()) - this.conenctStartTime);
        YYReporterUtil.add('cnct_fail', 1);

        YYReporterUtil.addStep('cnct_fail_time_' + this.currentIp, (new Date().valueOf()) - this.conenctStartTime);
        YYReporterUtil.add('cnct_fail_' + this.currentIp, 1);

        this._onConnectFailure();
        if (this._failureCb) {
            this._failureCb();
        }
    },
    /**
     * 获取ping值
     * @return {Number}
     * @public
     */
    getLastPingInterval: function () {
        if (this._isConnected) {
            return this._client.getLastPingInterval();
        }
        return 16888;
    },
    /**
     * 断开
     * @param {Boolean} [is_hall] default:false
     * @public
     */
    disconnect: function (is_hall) {
        this._is_hall = is_hall;
        cc.log('主动断线');
        YYReporterUtil.add(ReporterType.Disconnect_Active, 1);
        this.clearMsgs();
        if (this._isConnected) {
            this._client.disconnect();
        }
    },
    /**
     * 设置断开回调
     * @param {Function} cb
     * @public
     */
    setOnDisconnectListener: function (cb) {
        this._disconnectCb = cb;
    },

    removeOnDisconnectListener: function () {
        this._disconnectCb = null;
    },

    /**
     * 发送go服务器数据
     * @param msg
     * @public
     */
    wsData: function (msg) {
        this._sendPhz(5000, msg);
    },
    /**
     * 是否联接状态
     * @return {Boolean}
     * @public
     */
    isAlive: function () {
        return this._isConnected;
    },
    debugMsg: function (code, msg) {
        eventManager.dispatchEvent('GS_Net_' + code, msg);
    }
});
